#include "RedisCli.h"

bool RedisCli::HSET_val(const std::string &table, const std::string &key, const std::string &value)
{

    redisContext *c;
    c = redisConnect("127.0.0.1", 6379);
    if(c == NULL){
        printf("Connection error: %s\n", c->errstr);
        redisFree(c);
    }
    // 选择数据库15
    redisReply *reply1 = static_cast<redisReply *>(redisCommand(c, "SELECT 15"));
    if (reply1 == nullptr){
        printf("redisCommand:SELECT 15 error: %s\n", c->errstr);
        freeReplyObject(reply1);
        return false;
    }
    freeReplyObject(reply1);

    redisReply *reply3 = (redisReply *)redisCommand(c, "HSET %s %s %s", table.c_str(), key.c_str(), value.c_str());
    if (reply3 == nullptr){
        printf("redisCommand:HSET %s error: %s\n", key.c_str(), c->errstr);
        freeReplyObject(reply3);
        return false;
    }
    freeReplyObject(reply3);


    Json::Value message_data;
    message_data["operation"] = "hset";
    message_data["table_name"] = table.c_str();
    message_data["field"] = key.c_str();
    message_data["value"] = value;

    
    Json::StreamWriterBuilder writer;
    writer["indentation"] = "";
    std::string message = Json::writeString(writer, message_data);
    
    redisReply* reply4 = (redisReply*)redisCommand(c, "PUBLISH __keyspace@15__:table %s", message.c_str());
    if (reply4 == nullptr)
    {
        
        printf("Failed to publish message to channel\n");
        return false;
    }

    // 释放 Redis 响应对象
    freeReplyObject(reply4);


    return true;
}

bool RedisCli::HGET_all_val(const std::string &table, std::vector<std::string> &result_arr)
{

    redisContext *c;
    c = redisConnect("127.0.0.1", 6379);
    if(c == NULL){
        printf("Connection error: %s\n", c->errstr);
        redisFree(c);
    }
    // 选择数据库15
    redisReply *reply1 = static_cast<redisReply *>(redisCommand(c, "SELECT 15"));
    if (reply1 == nullptr){
        printf("redisCommand:SELECT 15 error: %s\n", c->errstr);
        freeReplyObject(reply1);
        return false;
    }
    freeReplyObject(reply1);

    redisReply *reply2 = (redisReply *)redisCommand(c, "HGETALL %s", table.c_str());
    if (reply2 == nullptr){
        printf("redisCommand:HGETALL error: %s\n", c->errstr);
        freeReplyObject(reply2);
        return false;
    }
    result_arr.clear();
    for (size_t i = 0; i < reply2->elements; i += 2){
        std::string field = reply2->element[i]->str;
        std::string val = reply2->element[i + 1]->str;
        std::cout << "Field: " << field << ", Value: " << val << std::endl;
        result_arr.push_back(field);
        result_arr.push_back(val);
    }
    // 释放返回的 reply 对象
    freeReplyObject(reply2);
    return true;
}

bool RedisCli::WATCH(const std::string &table)
{
    std::ofstream outfile;
    outfile.open("watchlog.txt");

    redisContext *c;
    c = redisConnect("127.0.0.1", 6379);
    if(c == NULL){
        printf("Connection error: %s\n", c->errstr);
        redisFree(c);
    }
    // 选择数据库15
    redisReply *reply1 = static_cast<redisReply *>(redisCommand(c, "SELECT 15"));
    if (reply1 == nullptr){
        printf("redisCommand:SELECT 15 error: %s\n", c->errstr);
        freeReplyObject(reply1);
        return false;
    }
    freeReplyObject(reply1);

    redisReply *reply2 = static_cast<redisReply *>(redisCommand(c, "CONFIG SET notify-keyspace-events KEA"));
    if (!reply2){
        printf("redisCommand:CONFIG SET ERROR: %s\n", c->errstr);
        freeReplyObject(reply2);
        return false;
    }
    freeReplyObject(reply2);

    // 订阅频道
    const std::string channel = "__keyspace@15__:" + table;
    redisReply *sub_reply = (redisReply *)redisCommand(c, "SUBSCRIBE %s", channel.c_str());
    if (sub_reply == nullptr){
        printf("redisCommand:SUBSCRIBE ERROR: %s\n", c->errstr);
        freeReplyObject(sub_reply);
        return false;
    }
    freeReplyObject(sub_reply);

    // 监听事件
    while(true){
         
        void * reply3; 
         //redisReply *reply3;
        if (redisGetReply(c, &reply3) != REDIS_OK){

            continue;
        }
        else{
            redisReply *reply = (redisReply *)reply3;
            // 检查通知的类型是否为数组
            if (reply->type == REDIS_REPLY_ARRAY){

                // 检查数组元素的数量是否足够
                if (reply->elements >= 3 && reply->element[2]->type == REDIS_REPLY_STRING){

                    // 使用 strcmp 比较字符串是否相等
                    if (strcmp(reply->element[2]->str, "hset") == 0){

                        std::vector<std::string> Event_arr;
                        HGET_all_val(table, Event_arr);
                        if(Event_arr.empty()){
                            outfile << "in else if Event empty" <<std::endl;
                            freeReplyObject(reply3);
                            continue;
                        }

                        //处理缺项事件
                        if(!lack_callback(Event_arr)){
                            //std::cout << "rediscli watch函数:缺项事件处理失败" << std::endl;
                        }
                        freeReplyObject(reply3);

                    }else{freeReplyObject(reply3);}
                }else{freeReplyObject(reply3);}
            }else{freeReplyObject(reply3);}
            
        }

        outfile << "in while true" <<std::endl;
        
    }
}


bool RedisCli::lack_callback(const std::vector<std::string> &Event_arr)
{
    std::cout << "进入标识缺项处理函数：" << std::endl; 
	//解析vector中内容
	//组建查询请求
	//下发表项
    bool flag = true;
	auto it = Event_arr.begin();
	while(it != Event_arr.end()){

		//迭代器取值
		std::string cid = *it;
        ++it;
        std::string j_pktid = *it;
        ++it;

		Json::Value object;
		Json::Reader reader;
		if(!reader.parse(j_pktid, object)){
            std::cout << "rediscli缺项处理函数:解析缺项pktid失败:" << cid << std::endl;
            flag = false;
            continue;
        }
		
		Json::Value reqData;
		reqData["cid"] = cid;
        std::cout << "标识缺项处理函数:向服务器请求缺项信息:" << cid << std::endl;
		Json::Value response = Functions::send_post_request("http://127.0.0.1:40123", reqData.toStyledString());
		//test：
		std::cout << "rediscli缺项处理函数:已查询相应信息并接收数据:" << std::endl << reqData << std::endl << "rsponse:" << response["rid"].asString() << response["mac"].asString() << response["ifn"].asString();
		if(response["err_no"].asInt() != 0){
            std::cout << "rediscli缺项处理函数:此cid相关信息查询失败:" << cid << std::endl;
            flag = false;
            continue;
        }

        //判断服务标识位
        std::string service_id = response["service_id"].asString();
        int check = 0, cache = 0;
        check = Functions::getCheck(service_id);
        cache = Functions::getCache(service_id);
        
        Json::Value jsonData;
        jsonData["rid"] = response["rid"];
        jsonData["mac"] = response["mac"];
        jsonData["ifn"] = response["ifn"];
        jsonData["pktid"] = object["pktid"];
        jsonData["multicast"] = 0;
        jsonData["check"] = check;
        jsonData["cache"] = cache;
        Json::StreamWriterBuilder writer;
        writer["indentation"] = "";
        std::string jsonStr = Json::writeString(writer, jsonData);
        // std::string jsonStr = jsonData.toStyledString();

        //缺项下发cid表项:
        std::cout << "rediscli缺项处理函数:缺项下发cid表项:" << object["pktid"].asInt() << std::endl << (char*)cid.c_str() << std::endl ;
        
        if(response.isMember("rid") && response.isMember("mac") && response["mac"].asString() != ""){  //判断是否取到值
            std::cout << (response["mac"].asString() != "null") << std::endl;
            std::cout << response << std::endl;


            if(!RedisCli::HSET_val("SLN_CID_TABLE", cid, jsonStr)){
                std::cout << "rediscli缺项处理函数:下发失败:" << cid << std::endl;
                flag = false;
            }
            sln_add_cid(object["pktid"].asInt(), (char*)cid.c_str(), (char*)response["rid"].asCString(), cache, check, (char*)response["mac"].asCString(), (char*)response["ifn"].asCString());
        
        }
        else {
            std::cout << "标识缺项处理：没有下发该cid对应表项" <<std::endl;
            flag = false;
        }

        
        

	}
    std::cout << "退出缺项处理" << std::endl;
    return flag;
}
